Skip to content

Add trusted-server-adapter-axum native dev server (PR 16)#643

Open
prk-Jr wants to merge 43 commits into
feature/edgezero-pr15-remove-fastly-corefrom
feature/edgezero-pr16-axum-dev-server
Open

Add trusted-server-adapter-axum native dev server (PR 16)#643
prk-Jr wants to merge 43 commits into
feature/edgezero-pr15-remove-fastly-corefrom
feature/edgezero-pr16-axum-dev-server

Conversation

@prk-Jr

@prk-Jr prk-Jr commented Apr 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds trusted-server-adapter-axum as a native (non-WASM) dev server so the full trusted-server pipeline can be run and tested locally without Fastly Compute or Viceroy
  • Promotes the axum adapter to a first-class workspace member by removing the global target = "wasm32-wasip1" override from .cargo/config.toml; Fastly-specific commands now pass --target wasm32-wasip1 explicitly
  • Extends the integration test matrix so WordPress and Next.js scenarios run against both Fastly (Viceroy) and Axum, verifying identical behaviour across platforms

Changes

File Change
crates/trusted-server-adapter-axum/src/platform.rs PlatformConfigStore, PlatformSecretStore, PlatformBackend, PlatformGeo, PlatformHttpClient — env-var-backed implementations
crates/trusted-server-adapter-axum/src/middleware.rs FinalizeResponseMiddleware + AuthMiddleware — mirrors Fastly adapter, always emits X-Geo-Info-Available: false
crates/trusted-server-adapter-axum/src/app.rs TrustedServerApp implementing Hooks with all 11 routes wired
crates/trusted-server-adapter-axum/src/main.rs + axum.toml Binary entry point; config drives port (default 8787)
crates/trusted-server-adapter-axum/tests/routes.rs 8 integration tests via EdgeZeroAxumService (no live TCP server)
crates/integration-tests/tests/environments/axum.rs AxumDevServer runtime environment added to the matrix
crates/integration-tests/tests/environments/mod.rs Register AxumDevServer alongside FastlyViceroy
crates/integration-tests/tests/integration.rs test_wordpress_axum + test_nextjs_axum individual test functions
scripts/integration-tests.sh Also builds the Axum native binary with test-specific env vars
.cargo/config.toml Remove global target = "wasm32-wasip1"; keep only the viceroy runner
Cargo.toml Move trusted-server-adapter-axum from [exclude] to [members]
crates/trusted-server-adapter-axum/Cargo.toml Adopt workspace deps + lints
.github/workflows/test.yml Add test-axum CI job; test-rust now passes --target wasm32-wasip1 explicitly
CLAUDE.md Document axum adapter in workspace layout + build commands
.gitignore (root + adapter) Ignore .edgezero/ (local KV store created by dev server)

Closes

Closes #497

Test plan

  • cargo test --workspace (Fastly/WASM crates via Viceroy)
  • cargo test -p trusted-server-adapter-axum (8 route + middleware tests)
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • JS tests: cd crates/js/lib && npx vitest run (282 tests)
  • Integration tests: test_wordpress_fastly, test_nextjs_fastly, test_wordpress_axum, test_nextjs_axum all pass
  • Manual testing: cargo run -p trusted-server-adapter-axum starts on port 8787

Checklist

  • Changes follow CLAUDE.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses log macros (not println!)
  • New code has tests
  • No secrets or credentials committed

prk-Jr added 12 commits April 17, 2026 11:13
Move trusted-server-adapter-axum from workspace exclude to members list.
Remove the global `target = "wasm32-wasip1"` build override from
.cargo/config.toml (which forced the axum crate out of the workspace)
and pass --target wasm32-wasip1 explicitly only for Fastly CI commands.
Delete the now-redundant crate-local .cargo/config.toml.

Update CI test-rust job to exclude the axum crate and pass the explicit
target; test-axum job runs from the workspace root with -p flag.
Add .edgezero/ to .gitignore to exclude the local KV store file.
Register AxumDevServer alongside FastlyViceroy in RUNTIME_ENVIRONMENTS so
the full framework x runtime scenario matrix (WordPress, Next.js) runs
against both platforms.

AxumDevServer spawns the native trusted-server-axum binary (no WASM or
Viceroy), binds to the fixed port 8787 (baked into axum.toml at compile
time), and polls for any HTTP response as readiness (root returns 403 in
test env). Binary path defaults to target/debug/trusted-server-axum,
overridable via AXUM_BINARY_PATH.

Settings are baked in at build time via TRUSTED_SERVER__* env vars, same
as Fastly. The integration-tests.sh script now builds both the WASM and
the native Axum binary with test-specific overrides (origin=127.0.0.1:8888).

Add test_wordpress_axum and test_nextjs_axum individual test functions.
Ignore .edgezero/ at workspace root (local KV store from the dev server).
@prk-Jr prk-Jr self-assigned this Apr 17, 2026
@prk-Jr prk-Jr linked an issue Apr 17, 2026 that may be closed by this pull request
prk-Jr added 3 commits April 17, 2026 13:42
- README: update Quick Start and Development commands for both runtimes
- getting-started: add Axum as Option A (no Fastly CLI needed)
- architecture: add trusted-server-adapter-axum to Core Components;
  add Runtime Targets table
- testing: fix cargo test commands; add Axum adapter section; split
  Local Server Testing into Axum vs Fastly; restore clippy step in
  CI/CD workflow example alongside new test-axum job
The integration test matrix includes AxumDevServer which requires the
native trusted-server-axum binary. Add build-axum step to the shared
setup action, package the binary alongside the WASM artifact, and pass
AXUM_BINARY_PATH to the integration test run step.
@prk-Jr prk-Jr marked this pull request as draft April 17, 2026 08:27
@prk-Jr prk-Jr marked this pull request as ready for review April 18, 2026 14:14
@prk-Jr prk-Jr requested review from ChristianPavilonis and aram356 and removed request for aram356 April 18, 2026 14:14

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Introduces a native Axum dev-server adapter that runs the full trusted-server pipeline locally without Fastly Compute or Viceroy, promotes the crate to a workspace member, and extends the integration-test matrix to cover both runtimes. Requesting changes for a handful of small but concrete issues: unused deps, stale lockfile, doc/code drift, and a middleware bypass on the startup-error path.

Blocking

🔧 wrench

  • Unused direct dependencies: serde_json and trusted-server-js declared in crates/trusted-server-adapter-axum/Cargo.toml but never referenced in src/ or tests/. See inline.
  • Stale crate-local Cargo.lock: ~100 KB file that cargo now silently ignores (crate is a workspace member). Delete it to avoid lockfile drift.
  • CLAUDE.md comment contradicts the PR: says "excluded from workspace" at line 14 while the PR makes it a workspace member. See inline.
  • Log text references a non-existent NoopKvStore: the code uses UnavailableKvStore (src/platform.rs:367 vs :379). See inline.
  • Startup-error router bypasses middleware: startup_error_router() has no FinalizeResponseMiddleware / AuthMiddleware, breaking the "every response has X-Geo-Info-Available" invariant and bypassing operator headers + basic auth on startup-failure responses. See inline on src/app.rs:134.

Non-blocking

🤔 thinking

  • send_async/select diverges from Fastly (eager execution changes error-surface timing and ordering). Trade-off is documented, but consider a breadcrumb log or real tokio::spawn fan-out. See inline.
  • Body::Stream outbound body silently truncated to empty on axum. See inline.
  • Env-var namespace collisions possible due to -/./ _ normalization. See inline.
  • Fixed port 8787 baked at compile time can TIME_WAIT-flake across sequential integration tests. See inline.

♻️ refactor

  • reqwest::Client rebuilt per request — defeats connection pool. Move to shared Arc<AxumPlatformHttpClient> in AppState. See inline.
  • Env-var tests not isolated — use temp-env (already a workspace dep). See inline.
  • bytes::BytesVec<u8> round-trip on request/response bodies is wasted allocation. See inline.
  • tests/routes.rs uses .unwrap() instead of .expect("should ...") — CLAUDE.md applies to test code. See inline.

🌱 seedling

  • No /health endpoint — AxumDevServer::health_check_path() returns /health but the app never registers it; tests work around with wait_for_any_response. See inline.

⛏ nitpick

  • Crate-local .gitignore is redundant with the workspace .gitignore. See inline.
  • build_per_request_services is a no-op wrapper around build_runtime_services. See inline.

📝 note

  • test-axum CI job runs without TRUSTED_SERVER__* env vars — every request goes through the startup-error path. Smoke tests still pass, but not a great signal. See inline.

CI Status

  • fmt: PASS (verified locally, cargo fmt --all -- --check)
  • clippy (axum crate, host target): PASS with zero warnings
  • cargo test -p trusted-server-adapter-axum: 18 tests PASS
  • GitHub CI on 75fe0d01: prepare artifacts + integration tests + browser tests all PASS

Comment thread crates/trusted-server-adapter-axum/Cargo.toml Outdated
Comment thread crates/trusted-server-adapter-axum/Cargo.lock Outdated
Comment thread CLAUDE.md Outdated
Comment thread crates/trusted-server-adapter-axum/src/platform.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/app.rs
Comment thread crates/integration-tests/tests/environments/axum.rs Outdated
Comment thread crates/integration-tests/tests/environments/axum.rs
Comment thread crates/trusted-server-adapter-axum/.gitignore Outdated
Comment thread crates/trusted-server-adapter-axum/src/app.rs Outdated
Comment thread .github/workflows/test.yml Outdated
prk-Jr added 3 commits April 21, 2026 12:48
… bypass, refactors

Blocking:
- Remove unused serde_json and trusted-server-js from Cargo.toml
- Delete crate-local Cargo.lock (now silently ignored as workspace member)
- Fix CLAUDE.md workspace layout comment (drop "excluded from workspace")
- Fix log message naming NoopKvStore → UnavailableKvStore in build_runtime_services
- Wrap startup_error_router with FinalizeResponseMiddleware(Settings::default()) so
  startup-error responses carry X-Geo-Info-Available and operator response_headers

Refactor:
- Move AxumPlatformHttpClient to AppState; share Arc across requests via
  build_runtime_services(ctx, Arc::clone(&state.http_client)) to preserve
  the reqwest connection pool
- Remove build_per_request_services no-op wrapper; inline at call sites
- Use temp_env::with_var in config/secret store tests for proper isolation
- Replace .unwrap() with .expect("should ...") throughout test code per CLAUDE.md

Nitpick:
- Delete redundant crate-local .gitignore (covered by workspace .gitignore)
…ce breadcrumbs

Port fix:
- main.rs reads PORT env var at startup; when set, uses AxumDevServer::with_config
  with a dynamic SocketAddr instead of the run_app default (hardcoded 8787)
- Integration test spawner (axum.rs) now calls find_available_port() and passes
  PORT=<port> to the child process, matching the Fastly env's dynamic-port pattern
- Fallback to AXUM_DEFAULT_PORT=8787 if find_available_port fails (offline runner)

Divergence breadcrumbs:
- send_async: debug log noting that execution is eager and errors surface immediately,
  not at select() time as they do on Fastly
- select: debug log noting that index 0 is popped unconditionally (sequential, not
  first-to-complete) — any fan-out ordering tests should use the Fastly runtime

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Nice addition overall — the Axum adapter is headed in a useful direction. I found a few correctness gaps in the platform shim plus a couple of documentation mismatches around what the Axum runtime can currently support.

Comment thread crates/trusted-server-adapter-axum/src/platform.rs
Comment thread crates/trusted-server-adapter-axum/src/platform.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/app.rs Outdated
Comment thread docs/guide/getting-started.md
Comment thread docs/guide/architecture.md Outdated
@prk-Jr prk-Jr requested a review from ChristianPavilonis May 14, 2026 07:04

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Reviewed the Axum native dev server PR. I found a couple of Axum/Fastly parity issues worth addressing and several mixed-target workspace documentation updates. CI checks visible from gh pr checks are passing.

Body-only findings

Proxy response finalization repeats metadata and image heuristics

  • Severity: P2
  • File: crates/trusted-server-core/src/proxy.rs
  • Line: 200-405
  • Description: finalize_proxied_response() and finalize_proxied_response_streaming() duplicate response metadata extraction/logging and the image/pixel heuristic block. The only meaningful differences are content-encoding processing and the streaming log prefix, so changes to image handling or origin-response logging now require parallel edits.
  • Suggestion: Extract small helpers such as origin_response_metadata(req, &response) and apply_image_passthrough_metadata(req, target_url, &ct, &mut response, log_prefix), then keep only the HTML/CSS processing branch in the buffered finalizer.

Related stale-build-doc locations that may not be commentable inline: CLAUDE.md:42-43 also still shows bare cargo build.

Comment thread crates/trusted-server-adapter-axum/src/platform.rs
Comment thread crates/trusted-server-adapter-axum/src/platform.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/app.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/app.rs Outdated
Comment thread README.md Outdated
Comment thread docs/guide/getting-started.md Outdated
Comment thread docs/guide/testing.md Outdated
Comment thread docs/guide/testing.md Outdated
Comment thread crates/trusted-server-adapter-axum/src/lib.rs
Comment thread docs/guide/getting-started.md Outdated
- Disable reqwest auto-redirects in AxumPlatformHttpClient so core proxy
  code receives 3xx responses and applies its own allowed_domains policy
- Map select() task panics and per-request errors into ready: Err(...)
  so the auction orchestrator logs provider failures and continues rather
  than treating one bad provider as a fatal auction error
- Extract buffer_publisher_response into trusted_server_core::publisher;
  both adapters now call it, removing duplicated buffering logic
- Extract origin_response_metadata and apply_image_passthrough_metadata
  helpers in proxy.rs; both finalizers share one copy of metadata
  extraction and pixel heuristics
- Rewrite app.rs with NamedRouteHandler enum, execute_handler, and a
  named_routes table mirroring the Fastly adapter; remove per-route
  boilerplate closures (~180 lines)
- Add crate-level and per-module doc comments to axum adapter lib.rs
- Replace bare cargo build/test/clippy docs with target-specific aliases
  across README, getting-started, and testing guides
- Document PORT env var override for Axum dev server
@prk-Jr prk-Jr requested a review from ChristianPavilonis May 22, 2026 15:34

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

The current branch is in good validation shape (fmt, clippy-fastly, clippy-axum, test-fastly, and test-axum were reported passing), but I found two Fastly EdgeZero parity regressions plus one stale documentation/template issue. One finding is inline below; the other findings are included here because the referenced lines are outside the current PR diff and cannot be attached inline.

High (P1)

  1. Fastly EdgeZero path loses HTTPS scheme metadatacrates/trusted-server-adapter-fastly/src/app.rs:164

    build_per_request_services() sets tls_protocol / tls_cipher to None, and edgezero_main() strips spoofable forwarded headers before dispatch. That means RequestInfo::from_request() can fall back to http for Fastly EdgeZero publisher requests, so HTTPS pages may get rewritten URLs emitted as http://..., causing mixed-content/security regressions.

    Suggested fix: preserve trusted TLS metadata before dispatch, e.g. extend the Fastly EdgeZero request context or inject a trusted internal scheme signal after sanitizing client headers.

Low (P3)

  1. Active docs/templates still reference obsolete workspace commandsdocs/guide/integration-guide.md:259, .github/pull_request_template.md:26

    The branch updates most commands to cargo test-fastly / cargo test-axum and split clippy aliases, but the integration guide and PR template still instruct contributors to run cargo test --workspace / cargo clippy --workspace....

    Suggested fix: update these to the new target-specific aliases.

Comment thread crates/trusted-server-adapter-fastly/src/app.rs Outdated
Comment thread crates/trusted-server-adapter-fastly/src/app.rs Outdated
Fix EdgeZero HTTPS scheme detection regression: capture tls_protocol and
tls_cipher from the raw FastlyRequest before dispatch_with_config_handle
consumes it, inject as trusted internal headers x-ts-tls-protocol /
x-ts-tls-cipher (added to INTERNAL_HEADERS), and read them in
build_per_request_services to populate ClientInfo. This restores parity with
the legacy path where RequestInfo::from_request detects HTTPS via ClientInfo
TLS fields rather than falling back to the default "http".

Skip the from_fastly_response -> to_fastly_response round-trip for
middleware-finalized responses in edgezero_main. Checking x-ts-finalized
on the FastlyResponse directly and calling send_to_client() without the
intermediate take_body_bytes() call avoids re-materializing the full response
body in WASM heap on the normal registered-route path.

Update integration-guide.md and pull_request_template.md to use the
target-specific cargo aliases (cargo test-fastly / cargo test-axum,
cargo clippy-fastly / cargo clippy-axum) instead of the removed
--workspace variants.
@prk-Jr prk-Jr requested a review from ChristianPavilonis May 23, 2026 17:23

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Re-review of PR16 (Axum dev-server adapter) at head a6e1073b. Most prior findings from earlier reviews are resolved; CI is green. This re-review finds one new blocking security issue introduced by the most recent commit (TLS-header trust contract not enforced) plus several non-blocking concerns.

Note: PR base is feature/edgezero-pr15-remove-fastly-core, not main. PR15 must merge first; ordering should be confirmed before merge of this PR.

Blocking

🔧 wrench

  • Spoofable x-ts-tls-protocol / x-ts-tls-cipher on the EdgeZero entry path — see inline at crates/trusted-server-adapter-fastly/src/main.rs:178. The trust contract documented at L173-176 is not enforced; the spoofable-headers strip list does not include x-ts-tls-*, and the trusted-source set_header is conditional on the Fastly SDK returning Some. Consequence: detect_request_scheme can be spoofed to https over plain HTTP, affecting cookie Secure, URL rewriting, and any scheme-gated handler.

Non-blocking

🤔 thinking

♻️ refactor

  • Stateless platform shims allocated Arc::new(...) per request — see inline at platform.rs:497.
  • Two divergent code paths in main.rs::main() — see inline at main.rs:16.

⛏ nitpick

  • Logger init failure silently swallowed in main() — see inline at main.rs:9.
  • PORT parse error falls back instead of failing fast — see inline at main.rs:43.

🌱 seedling

📌 out of scope

  • Consolidate known dev-server limitations in crates/trusted-server-adapter-axum/src/lib.rs. The PR author's responses to prior reviews acknowledge several intentional Axum-vs-Fastly divergences (eager send_async, sequential select ordering, truncated Body::Stream outbound, no KV/geo, env-var key collision surface). Today they're scattered across platform.rs, app.rs, and middleware.rs doc comments — a single "Known limitations" section in lib.rs would make them discoverable. Worth a follow-up issue rather than this PR.

CI Status

  • fmt: PASS
  • clippy (fastly + axum): PASS
  • rust tests (Fastly via Viceroy + Axum native): PASS
  • integration tests: PASS
  • browser integration tests: PASS

All 3 PR checks green at head a6e1073b.

Comment thread crates/trusted-server-adapter-fastly/src/main.rs
Comment thread crates/trusted-server-adapter-axum/src/platform.rs
Comment thread crates/integration-tests/tests/environments/axum.rs
Comment thread crates/trusted-server-adapter-axum/src/platform.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/main.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/main.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/main.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/middleware.rs
Blocking:
- Strip x-ts-tls-protocol and x-ts-tls-cipher from the inbound request
  before conditionally re-setting them from the Fastly SDK in edgezero_main;
  without this, a plain-HTTP client injecting X-TS-TLS-Protocol spoofs the
  scheme detection path, affecting cookie Secure and URL rewriting
- Add regression test in app.rs asserting tls_protocol/tls_cipher are None
  when the trusted headers are absent after the strip+set logic

Non-blocking:
- Register GET /health -> 200 ok on TrustedServerApp so health_check_path()
  is satisfied and the shared wait_for_ready helper can be used
- Promote stateless shims (AxumPlatformConfigStore, AxumPlatformSecretStore,
  UnavailableKvStore, AxumPlatformBackend, AxumPlatformGeo) to OnceLock
  statics in build_runtime_services so Arc::clone is used per request
  instead of allocating a fresh Arc each time
- Unify the two divergent main() branches into a single code path using
  AxumDevServer::with_config; log logger init failure with eprintln! instead
  of silently swallowing it
- Exit non-zero on PORT parse error instead of falling back silently to
  axum.toml default — the fallback surprises tooling expecting the server
  at the explicitly requested port
@prk-Jr

prk-Jr commented May 28, 2026

Copy link
Copy Markdown
Collaborator Author

All round-2 findings resolved in commit 746993c.

Blocking — fixed

  • Spoofable x-ts-tls-* headers: added req.remove_header("x-ts-tls-protocol") and req.remove_header("x-ts-tls-cipher") unconditionally before the conditional set_header calls. Client-injected headers on plain-HTTP requests are stripped; trusted SDK values are only set when Fastly returns Some. Regression test build_per_request_services_does_not_trust_client_supplied_tls_headers pinned in app.rs.

Non-blocking — addressed

  • /health route: registered GET /health -> 200 ok on TrustedServerApp::routes(). health_check_path() now resolves and the wait_for_any_response workaround in axum.rs can be replaced with the shared wait_for_ready helper.
  • Stateless shims per-request: all five shims (AxumPlatformConfigStore, AxumPlatformSecretStore, UnavailableKvStore, AxumPlatformBackend, AxumPlatformGeo) promoted to OnceLock<Arc<dyn Trait>> statics; call sites clone an existing Arc instead of allocating.
  • Duplicate main() branches: unified into a single AxumDevServer::with_config path; ~15 lines removed.
  • Logger init: failure surfaces via eprintln! instead of being silently dropped.
  • PORT parse error: exits non-zero with a clear message instead of falling back silently to the axum.toml default.

Deferred

  • reqwest::Client per-request: kept as-is with the documented rationale (Next.js regression). Follow-up issue to investigate whether root cause is a body-buffering bug or response body not driven to completion.
  • Operator-header validation at startup: same pattern exists in the Fastly adapter; cross-adapter cleanup tracked as a follow-up.
  • Known-limitations in lib.rs: deferred per the 📌 out-of-scope note.

CI: cargo fmt, cargo clippy -D warnings (both adapters), cargo test (Axum native + core) all pass.

prk-Jr added 3 commits May 28, 2026 11:36
Conflicts resolved:

- app.rs (2 doc conflicts): PR16 wins on the module-level and
  build_per_request_services docs — they correctly describe the x-ts-tls-*
  header injection mechanism added in this PR
- app.rs (test import): merge both — PR16's build_per_request_services and
  PR15's build_state_from_settings both needed in the test module
- main.rs (edgezero_main finalization): PR15 wins — take_finalize_sentinel +
  inlined resolve_geo_for_response + apply_finalize_headers replaces the
  older apply_entry_point_finalize approach
- main.rs (function): PR15 wins — take_finalize_sentinel function replaces
  apply_entry_point_finalize
- main.rs (BoundedWriter + resolve_publisher_response_buffered): PR15 wins
  — add these structures needed for the EdgeZero buffered publisher path
- main.rs (test): PR15 wins — add take_finalize_sentinel_strips_sentinel test
- publisher.rs (doc): PR16 wins — buffer_publisher_response description is
  correct for that function; PR15 doc was misattributed to stream_publisher_body
The merge conflict resolution took PR15's take_finalize_sentinel code
expecting an HttpResponse but left the fastly_response variable unconverted.
Add the missing let mut response = compat::from_fastly_response(fastly_response)
before the sentinel check.
- Remove BoundedWriter and resolve_publisher_response_buffered from main.rs;
  they were copied in by the merge but belong in app.rs for the EdgeZero path;
  the legacy path uses stream_publisher_body directly
- Remove the now-unused std::io::Write import
- Fix let mut fastly_response to let fastly_response (no longer mutated)

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Round-4 re-review of the Axum dev-server adapter, scoped to the PR's own diff against its base feature/edgezero-pr15-remove-fastly-core. All blocking findings from the previous three rounds are resolved (deps, lockfile, middleware bypass, fan-out semantics, Body::Stream buffering, env-var rename, fmt/clippy failures, CLAUDE.md/AGENTS.md/agent-doc drift, wasm clippy coverage, fixed-port flakiness, /health registration, handler boilerplate). Verified locally: cargo fmt --all -- --check passes, clippy -p trusted-server-adapter-axum is clean, and the 19 axum tests pass.

Requesting changes for one regression introduced by this PR: the removal of the publisher buffered-body size cap.

Blocking

🔧 wrench

  • Unbounded publisher buffer / dead max_buffered_body_bytes: deleting resolve_publisher_response_buffered (and its BoundedWriter) in favor of the new unbounded buffer_publisher_response drops settings.publisher.max_buffered_body_bytes enforcement on the Fastly EdgeZero path. The setting is still defined, defaulted to 16 MiB, and documented as preventing Wasm-heap OOM, but is now read nowhere. See inline on crates/trusted-server-core/src/publisher.rs:418.

Non-blocking

📌 out of scope

  • Per-request reqwest::Client (crates/trusted-server-adapter-axum/src/platform.rs:521): the no-pooling revert is deliberate, but its comment cites a Next.js "poisoned connection after a truncated POST." The empty-body branch (if !body_bytes.is_empty()) skipping Content-Length: 0 is a likely latent cause worth a follow-up issue rather than masking with per-request clients.

📝 note

  • Weak happy-path coverage: the test-axum CI job runs without TRUSTED_SERVER__* env vars and tests/routes.rs only asserts non-404/non-5xx, so requests flow through startup_error_router and 200/302 paths are never exercised. Pre-existing; deferred.

⛏ nitpick

  • Cargo.toml:83: reqwest added out of alphabetical order (sits before regex).
  • crates/trusted-server-adapter-axum/Cargo.toml:33-35: edgezero-adapter-axum, edgezero-core, and reqwest are re-declared in [dev-dependencies] though already normal dependencies with no added features — redundant.

CI Status (verified locally on macOS host)

  • fmt: PASS (cargo fmt --all -- --check)
  • clippy (axum, native): PASS (zero warnings)
  • cargo test -p trusted-server-adapter-axum: PASS (19 tests)
  • GitHub CI (integration tests, browser integration tests): PASS
  • Note: Run Tests / Run Format workflows only trigger on PRs to main, so fmt/clippy/test-fastly/test-axum have not run on this PR's feature-branch target; they were verified locally instead.

Comment thread crates/trusted-server-core/src/publisher.rs Outdated

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by Yesman.

I found two blocking issues that should be fixed before merge: the Axum async select path can mislabel remaining backend requests after the first completion, and the shared publisher buffering helper drops the configured max-buffer guard that protected the Fastly EdgeZero path from unbounded buffering. Inline comments include details.

Comment thread crates/trusted-server-adapter-axum/src/platform.rs Outdated
Comment thread crates/trusted-server-core/src/publisher.rs Outdated
prk-Jr added 6 commits June 8, 2026 19:29
Pick up PR15 commits since the first merge (cedea8b — remove stale
fastly::Body reference from platform/mod.rs docs).

Conflicts resolved:

- Cargo.lock: auto-resolved, take union
- integration-tests/tests/integration.rs: keep both Axum tests (PR16)
  and EC lifecycle test (PR15)
- trusted-server-adapter-fastly/src/main.rs: keep PR16 TLS
  strip/inject headers AND PR15 into_core_request + oneshot dispatch

Post-merge compilation fixes for trusted-server-adapter-axum:

- app.rs: update handle_proxy call to use ProxyDispatchInput struct
  (PR15 bundled 5 args into struct to stay within 7-arg cap)
- app.rs: update handle_auction call with new kv/registry/ec_context
  args added in PR15 (pass None/default for Axum dev server)
- platform.rs: add failed_backend_name: None to PlatformSelectResult
  initializer (new field added in PR15)
The PR15 merge reintroduced fastly into trusted-server-core through
ec/kv.rs, ec/rate_limiter.rs, and an orphaned backend.rs, breaking
native linking for the Axum dev server.

- Delete orphaned core backend.rs (adapter already owns its copy)
- Keep the RateLimiter trait in core; move FastlyRateLimiter and
  RATE_COUNTER_NAME to the Fastly adapter
- Add the EcKvStore primitive trait (lookup with generation marker,
  conditional insert, prefix count, delete) in ec/kv_backend.rs
- Rewrite KvIdentityGraph on top of EcKvStore so CAS retry loops,
  tombstone semantics, and validation stay in core
- Implement FastlyEcKvStore in the adapter mapping onto the Fastly
  KV Store API
- Add in-memory and failing test backends; cover create/revive,
  upsert, and tombstone paths natively
get_settings() now rejects the placeholder secrets baked into
trusted-server.toml (hardened in PR14/15) instead of only logging
warnings, so adapter tests that loaded baked settings began failing.

- Build test settings from explicit TOML with non-placeholder values
  in the Axum middleware tests and the Fastly app tests
- Add TrustedServerApp::routes_with_settings() so the Axum route
  integration tests drive the real router without the baked settings
- Accept the designed 501 Not Implemented for admin key routes on the
  dev server; only an unhandled 500 fails the test
- Restore the publisher buffered-body size cap: reinstate BoundedWriter
  (now in core so both adapters share it) and enforce
  settings.publisher.max_buffered_body_bytes in
  buffer_publisher_response, with unit tests covering the cap
- Fix backend-name misattribution in AxumPlatformHttpClient::select:
  futures::future::select_all uses swap_remove and makes no ordering
  guarantee for remaining futures, so positional index reconstruction
  could pair later auction responses with the wrong bidder. Each
  AxumPendingHandle now implements Future and resolves to its own
  backend name
- Reorder reqwest after regex in workspace Cargo.toml
- Drop redundant dev-dependency re-declarations (edgezero-adapter-axum,
  edgezero-core, reqwest) in the Axum adapter manifest
Removing the fastly dependency from trusted-server-core dropped its
transitive wasm dependencies from the integration-tests dependency
graph, leaving the crate's standalone Cargo.lock stale and failing the
--locked check in check-integration-dependency-versions.sh.
The 'Verify Fastly WASM release build' step builds for wasm32-wasip1
but the job's toolchain setup never installed that target, failing
with: can't find crate for core.

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Re-review of the Axum native dev-server adapter against its base (feature/edgezero-pr15-remove-fastly-core). Most prior blocking items are now genuinely fixed (verified): the select_all success-path backend-name misattribution, the unbounded publisher buffer / dead max_buffered_body_bytes cap, TLS-header spoofing strip, /health route, PORT exit-on-error, the clippy split covering both targets, and the SYNTHETIC__SECRET_KEYEDGE_COOKIE__SECRET_KEY rename. The new trait extractions (EcKvStore, RateLimiter) that remove fastly from core are well-executed and faithful to the original semantics.

Requesting changes for two items: a correctness regression on the auction error path (the counterpart of the select_all fix was left incomplete) and an incomplete migration of the workspace-command aliases into the live dev-tooling files.

Blocking

🔧 wrench

  • Axum auction: failed providers silently vanishcrates/trusted-server-adapter-axum/src/platform.rs:484 returns failed_backend_name: None on the error path, so a failed provider is never attributed and no BidStatus::Error is recorded (it disappears via the orchestrator's "backend not identified" branch). The Fastly adapter returns Some(failed_name). Cross-platform parity regression. See inline.

  • Live dev-tooling commands broken by the global-target removal — removing [build] target = wasm32-wasip1 plus making axum a workspace member means bare cargo {build,test,clippy} --workspace now compiles the wasm-only fastly crate for the host and fails. CLAUDE.md, AGENTS.md, README.md, and three agent docs were migrated to the *-fastly/*-axum aliases, but four live tooling files were missed (same root cause):

    • .claude/commands/check-ci.md:4-5 — the /check-ci command (cargo clippy/test --workspace)
    • .claude/commands/verify.md:3,6,7 — the /verify command (cargo build/clippy/test --workspace)
    • .claude/commands/test-all.md:4 — the /test-all command (cargo test --workspace)
    • .claude/agents/build-validator.md:14,26 (cargo build/clippy --workspace)

    A contributor running /check-ci or /verify now hits the exact failure AGENTS.md warns against. Update each to cargo test-fastly && cargo test-axum / cargo clippy-fastly && cargo clippy-axum.

Non-blocking

🤔 thinking

  • Dropped remaining auction handles detach, not abortplatform.rs:453; dev-only, up to 30s of wasted background work per dropped bidder. See inline.
  • Unbounded upstream response bufferingplatform.rs:348 (and send_async at ~413); no cap, no log on the response side. See inline.
  • Redundant double error-wrapping in KV pathsec/kv.rs:663 and :762 re-wrap with the same message the backend already attached. See inline.

♻️ refactor

  • Dead if over discarded boolproxy.rs:370; apply_image_passthrough_metadata returns a bool both call sites ignore. See inline.

🌱 seedling

  • CAS-conflict retry branches untestedec/kv.rs:331; no test injects a generation conflict, so the retry / concurrent-revive / exhaustion paths are uncovered. See inline.
  • Per-request reqwest::Client keeps the pool disabledplatform.rs:280; documented Next.js-regression workaround, follow-up issue acknowledged. See inline.

⛏ nitpick

  • Bare version strings for dev-depscrates/trusted-server-adapter-axum/Cargo.toml:31,34 (axum/tower) vs the workspace-pinning convention. See inline.
  • Weak route-test assertions / no auction fan-out testtests/routes.rs:118. See inline.

📝 note

  • Misplaced WASM-verify step.github/workflows/test.yml:77; lives in test-axum, only compiles because rust-toolchain.toml pins the target. See inline.
  • CI never exercised fmt/clippy/test on this PRtest.yml and format.yml are gated pull_request: branches: [main]; this PR targets a feature branch, so only integration-tests.yml ran (the green checks). The fmt/clippy/test-fastly/test-axum gates will run for the first time on retarget to main.
  • CLAUDE.md bare cargo check under Testing & Quality now fails at the workspace root; scope it with -p or an alias.

CI Status

  • integration tests / browser tests / prepare artifacts: PASS (on 4a2e1134)
  • fmt / clippy / test-fastly / test-axum: not run on this PR (workflows gated to PRs targeting main); clippy-split config verified correct locally

Comment thread crates/trusted-server-adapter-axum/src/platform.rs Outdated
Comment thread crates/trusted-server-adapter-axum/src/platform.rs
Comment thread crates/trusted-server-adapter-axum/src/platform.rs
Comment thread crates/trusted-server-adapter-axum/src/platform.rs
Comment thread crates/trusted-server-core/src/ec/kv.rs Outdated
Comment thread crates/trusted-server-core/src/ec/kv.rs
Comment thread crates/trusted-server-core/src/proxy.rs Outdated
Comment thread crates/trusted-server-adapter-axum/Cargo.toml Outdated
Comment thread crates/trusted-server-adapter-axum/tests/routes.rs
Comment thread .github/workflows/test.yml
prk-Jr added 2 commits June 12, 2026 20:02
Reconciles PR15's EC parity and review fixes with PR16's trait
extractions, preferring the PR16 implementation where both sides changed
the same code:

- maybe_identity_graph / require_identity_graph keep PR16's
  KvIdentityGraph::new(FastlyEcKvStore::new(...)) construction (now
  pub(crate) for app.rs), and app.rs EC dispatch uses the core
  buffer_publisher_response instead of re-adding the adapter-local
  BoundedWriter copy that PR16 had already moved into core
- core buffer_publisher_response adapted to the plain usize
  max_buffered_body_bytes from PR15 (the unbounded None arm is
  unrepresentable)
- EcFinalizeState carries a use_finalize_kv flag instead of the
  KvIdentityGraph itself: the graph now wraps a non-Sync dyn EcKvStore
  and cannot ride in response extensions, so edgezero_main rebuilds it
  from settings when the flag is set
- FastlyRateLimiter import moved to the adapter-local module per PR16's
  rate-limiter extraction
- Test helpers merged: PR16's TLS-header tests and minimal_state plus
  PR15's test_router and absolute-URI empty_request
- integration-tests lock keeps PR16's post-fastly-removal graph; shared
  dependency versions verified with the check script
Blocking:
- Attribute failed auction providers on the Axum select error path:
  failed_backend_name now carries the backend whose request failed,
  matching the Fastly adapter, so the orchestrator removes the provider
  and records BidStatus::Error instead of dropping it through the
  "backend not identified" branch. Adds a select-level regression test
  (request to a closed port) asserting the attribution
- Migrate the remaining live dev-tooling files to the per-target
  aliases: /check-ci, /verify, /test-all, and the build-validator agent
  now use cargo {build,clippy,test}-{fastly,axum}. New build-* and
  check-* aliases added to .cargo/config.toml; CLAUDE.md's bare
  cargo build / cargo check examples updated to the aliases

Non-blocking:
- Abort dropped auction tasks: AxumPendingHandle aborts its JoinHandle
  on drop so deadline-dropped bidders stop instead of running up to the
  30s transport timeout in the background
- Log buffered upstream response sizes on both the send and send_async
  paths so a large upstream is visible in dev instead of silently
  growing the heap
- Drop the redundant double error-wrapping in count_hash_prefix_keys
  and delete — the backend already attaches store context
- apply_image_passthrough_metadata returns () instead of a bool both
  call sites discarded
- axum and tower dev-dependencies moved to workspace pins
- Admin route tests assert the fixed 501 contract instead of != 404,
  and CAS-conflict paths are now covered: a conflict-injecting EcKvStore
  test backend exercises the retry-then-succeed, concurrent-revive
  short-circuit, and retry-exhaustion arms of create_or_revive
@prk-Jr

prk-Jr commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator Author

Re the review-summary items without inline threads (all in f7e8846 unless noted):

  • Live dev-tooling migration (blocking): /check-ci, /verify, /test-all, and the build-validator agent now use the per-target aliases (cargo {clippy,test}-{fastly,axum}). Added build-fastly/build-axum and check-fastly/check-axum aliases to .cargo/config.toml for the build/check steps those files needed, and updated CLAUDE.md's bare cargo build / cargo check examples to the aliases (the 📝 note).
  • Merge conflicts: the branch now merges the updated PR15 base (47b746e). Where both sides changed the same code the PR16 implementation won — KvIdentityGraph::new(FastlyEcKvStore::new(...)) and the core buffer_publisher_response (adapted to the plain-usize max_buffered_body_bytes from PR15, so the unbounded arm is gone) — and PR15's EC parity work (EC API routes, EC request-state prelude, entry-point EC finalization) is threaded through PR16's trait extractions.
  • Verified on the merged head: fmt clean, clippy-fastly + clippy-axum clean, test-fastly 70+1245 pass, test-axum 20 pass, shared-dependency check passes.

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completed the review and found one low-severity gap (P3, behavior parity) plus previously validated no P0/P1/P2 blockers.

  • [P3] (named_routes): routes are not mirrored in Axum (, , and related canonical variants). These routes currently route into generic fallback behavior rather than the handler logic used on Fastly.

Recommendation: consider adding canonical route entries in Axum’s route table (e.g., as aliases with same handlers), so production parity remains consistent and consumers hitting documented API paths get expected responses even in dev/runtime testing mode.

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completed the review and found one low-severity gap (P3, behavior parity) plus previously validated no P0/P1/P2 blockers.

  • [P3] crates/trusted-server-adapter-axum/src/app.rs (named_routes): _ts routes are not mirrored in Axum (/_ts/admin/keys/*, /_ts/api/v1/identify, /_ts/api/v1/batch-sync and related canonical variants). These routes currently route into generic fallback behavior rather than the handler logic used on Fastly.

Recommendation: add _ts canonical route entries in Axum’s route table (as aliases to existing handlers where behavior is shared), so production-like API paths remain consistent in Axum test/runtime mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Axum dev server entry point

3 participants